﻿using UnityEngine;
using UnityEditor;
using System.IO;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Text;

public class ExportAssetBundle
{
    public static string AssetBundlePath
    {
        get
        {
            return string.Format("{0}/{1}", Application.dataPath, EditorConfig.AB_STORE_PATH);
        }
    }

    public static void BuildAssetBundle(BuildTarget target, string pathDst, AssetBundleBuild[] datas)
    {
        FileHelper.CreateDirectory(pathDst);

        BuildAssetBundleOptions options = BuildAssetBundleOptions.None;
        if (!Config.AB_COMPRESS)
        {
            // 不压缩
            options |= BuildAssetBundleOptions.UncompressedAssetBundle;
        }

        BuildPipeline.BuildAssetBundles(pathDst, datas, options, target);
        AssetDatabase.Refresh();
    }

    public static void BuildAssetBundle(BuildTarget target, string pathDst)
    {
        FileHelper.CreateDirectory(pathDst);
        BuildAssetBundleOptions options = BuildAssetBundleOptions.DeterministicAssetBundle;
        if (!Config.AB_COMPRESS)
        {
            // 不压缩
            options |= BuildAssetBundleOptions.UncompressedAssetBundle;
        }

        BuildPipeline.BuildAssetBundles(pathDst, options, target);
        AssetDatabase.Refresh();
    }

    static void BuildAssetBundle(BuildTarget target, bool buildLua = true, bool reset = true)
    {
        //ImporterSprite.Reimport ();
        ImporterAssetBundle.Reimport(buildLua, reset);

        string pathDst = AssetBundlePath;
        BuildAssetBundle(target, AssetBundlePath);
    }

    // 拷贝所有的AssetBundle到StreamingAssets
    static void CopyAssetBundle()
    {
        string pathSrc = AssetBundlePath;
        string pathDst = Config.STREAMING_ASSETS_PATH;

        // 拷贝data
        string manifistName = EditorConfig.AB_STORE_PATH;
        manifistName = manifistName.Substring(manifistName.LastIndexOf("/"));

        string pathDataSrc = string.Format("{0}/{1}", pathSrc, manifistName);
        string pathDataDst = string.Format("{0}/{1}", pathDst, Config.MANIFEST_NAME);

        if (!Config.DATA_COMPRESS)
        {
            FileHelper.DeleteDirectory(pathDst);

            FileHelper.CopyDirectory(pathSrc, pathDst, Config.AB_SUFFIX);
            FileHelper.CopyFileTo(pathDataSrc, pathDataDst);
        }
        else
        {
            // 压缩资源
            string pathMD5 = string.Format("{0}/{1}/md5", Application.dataPath, EditorConfig.TEMP_PATH);
            Dictionary<string, string> oldmd5s = ReadMD5(pathMD5);

            if (oldmd5s.Count == 0)
            {
                FileHelper.DeleteDirectory(pathDst);
            }

            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            List<string> files = FileHelper.GetAllChildFiles(pathSrc, Config.AB_SUFFIX);

            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();

            // 清单文件
            PackFile(pathDataSrc, pathDataDst);

            if (!EditorConfig.COMPRESS_THREAD)
            {
                int index = 0;
                int count = files.Count + 1;
                foreach (string file in files)
                {
                    string shortPath = file.Replace(pathSrc, "").Substring(1);
                    string md5 = Md5Helper.GetFileMd5(file);
                    sb.AppendFormat("{0}:{1}\n", shortPath, md5);

                    if (!IsSameFile(shortPath, md5, oldmd5s))
                    {
                        string fileDst = file.Replace(pathSrc, pathDst);
                        PackFile(file, fileDst);
                    }
                    ++index;
                    EditorUtility.DisplayProgressBar("压缩中...", shortPath, Mathf.InverseLerp(0, count, index));
                }
            }
            else
            {
                ThreadParam param = new ThreadParam();
                param.pathSrc = pathSrc;
                param.pathDst = pathDst;
                param.files = files;
                param.index = 0;
                param.md5s = oldmd5s;
                param.sb = sb;
                param.lockd = new object();

                List<Thread> threads = new List<Thread>();
                for (int i = 0; i < SystemInfo.processorCount; ++i)
                {
                    Thread thread = new Thread(new ParameterizedThreadStart(OnThreadCompress));
                    thread.Start(param);

                    threads.Add(thread);
                }

                while (true)
                {
                    EditorUtility.DisplayProgressBar("压缩中...", string.Format("{0}/{1}", param.index, param.files.Count), Mathf.InverseLerp(0, param.files.Count, param.index));

                    bool hasAlive = false;
                    foreach (Thread thread in threads)
                    {
                        if (thread.IsAlive)
                        {
                            hasAlive = true;
                            break;
                        }
                    }

                    if (!hasAlive)
                    {
                        break;
                    }

                    Thread.Sleep(10);
                }
            }

            Debug.LogFormat("-->Compress UseTime: {0}", watch.ElapsedMilliseconds);

            FileHelper.SaveTextToFile(sb.ToString(), pathMD5);
            EditorUtility.ClearProgressBar();
        }

        AssetDatabase.Refresh();
    }

    class ThreadParam
    {
        public string pathSrc;
        public string pathDst;

        public List<string> files;
        public int index;
        public System.Text.StringBuilder sb;
        public Dictionary<string, string> md5s;

        public object lockd;
    }

    static bool IsNeedCompress(string filename)
    {
        if (filename.Contains(Config.LUA_PATCH))
        {
            // 跳过Lua补丁包
            return false;
        }

        return true;
    }

    static void OnThreadCompress(object arg)
    {
        ThreadParam param = arg as ThreadParam;
        while (true)
        {
            string file = "";
            string shortPath = "";
            string md5 = "";
            bool need = false;

            lock (param.lockd)
            {
                if (param.index >= param.files.Count)
                {
                    // 完成
                    break;
                }
                file = param.files[param.index];
                shortPath = file.Replace(param.pathSrc, "").Substring(1);

                need = IsNeedCompress(shortPath);

                if (need)
                {
                    md5 = Md5Helper.GetFileMd5(file);
                    param.sb.AppendFormat("{0}:{1}\n", shortPath, md5);
                }

                ++param.index;
            }

            if (need)
            {
                if (!IsSameFile(shortPath, md5, param.md5s))
                {
                    string fileDst = file.Replace(param.pathSrc, param.pathDst);
                    PackFile(file, fileDst);
                }
            }
        }
    }

    static void PackFile(string fileSrc, string fileDst)
    {
        FileHelper.CreateDirectoryFromFile(fileDst);
        FileHelper.DeleteFile(fileDst);

        LzmaHelper.Compress(fileSrc, fileDst);
    }

    static Dictionary<string, string> ReadMD5(string path)
    {
        Dictionary<string, string> datas = new Dictionary<string, string>();

        string text = FileHelper.ReadTextFromFile(path);
        string[] md5s = text.Split(new[] { "\n" }, System.StringSplitOptions.RemoveEmptyEntries);

        for (int i = 0; i < md5s.Length; ++i)
        {
            string[] temp = md5s[i].Split(new[] { ":" }, System.StringSplitOptions.RemoveEmptyEntries);
            if (temp.Length == 2)
            {
                datas.Add(temp[0], temp[1]);
            }
        }

        return datas;
    }

    static bool IsSameFile(string file, string md5, Dictionary<string, string> datas)
    {
        string oldmd5 = "";
        if (datas.TryGetValue(file, out oldmd5))
        {
            if (string.Equals(oldmd5, md5))
            {
                return true;
            }
        }

        return false;
    }

    public static void BuildInner(bool buildLua = true, bool reset = true)
    {
        BuildAssetBundle(EditorUserBuildSettings.activeBuildTarget, buildLua, reset);
        ConfigHelper.UpdatePackageCode();
        CopyAssetBundle();

        Debug.Log("AssetBundle Build Completed");
    }
}